<template>
	<BothScrollArea
		ref="scroll"
		:visible="showBar||autoScrollStatus!==0"
		class="lexmis-SelectArea"
		@resize="$_lexmis_resize"
		@mousedown.native="$_lexmis_mousedown"
		@mouseenter.native="showBar=true"
		@mouseleave.native="showBar=false">
		<template #default="{width}">
			<div
				ref="container"
				:class="{selecting}"
				class="lms-ScrollArea_container"
				:style="{minWidth:width+'px'}">
				<QVirtualScroll
					:scroll-target="scrollTarget"
					:virtual-scroll-slice-size="30"
					:virtual-scroll-item-size="lineHeight"
					:items="dataArr">
					<template #default="{index,item}">
						<slot v-bind="{allSelect,index,item}"></slot>
					</template>
				</QVirtualScroll>
				<div
					v-if="selecting"
					class="lexmis_rect"
					:style="selectRect"></div>
			</div>
		</template>
	</BothScrollArea>
</template>

<script>
import {QVirtualScroll} from 'quasar';
import BothScrollArea from '../both-scroll-area';
import {throttle} from 'lodash';
import {MySet} from '../../../util/polyfill';

/**
 * @author 唐国雄
 * 框选区域(支持shift,ctrl,模仿window模式)
 * 属性----------------------
 * v-model			已经选择的值
 * :data			所有列表数据
 * :line-height		数据行高
 * :disable			禁止框选
 * 插槽----------------------
 * #default			列表内容,{allSelect:MySet,index:number,item:any}
 * 事件----------------------
 * @resize			容器大小,{width:number,height:number}
 */
export default {
	name:'SelectArea',
	components:{
		BothScrollArea,
		QVirtualScroll,
	},
	props:{
		//每条数据的行高
		lineHeight:{
			type:Number,
			default:0
		},
		//列表数据
		data:{
			type:MySet,
			default(){
				return [];
			}
		},
		//选中的数据
		value:{
			type:MySet,
			default(){
				return [];
			}
		},
		//禁止框选
		disable:Boolean,
	},
	data(){
		return {
			//是否正在选择
			selecting:false,
			//开始选择的X
			startX:0,
			//开始选择的Y
			startY:0,
			//选择结束的X
			endX:0,
			//选择结束的Y
			endY:0,
			//0表示不动,1表示向上,2表示向下
			autoScrollStatus:0,
			//显示滚动条
			showBar:false,
			//滚动容器
			scrollTarget:null,
			//正在选择数据的开始索引
			startPos:0,
			//正在选择数据的结束索引
			endPos:0,
		};
	},
	computed:{
		//data的array形式
		dataArr(){
			return Array.from(this.data);
		},
		//正在选择的数据,(性能瓶颈)
		userSelect(){
			if(this.selecting){
				return this.dataArr.slice(this.startPos,this.endPos);
			}else{
				return [];
			}
		},
		//所有选中的数据(已经选择+正在选择),(性能瓶颈)
		allSelect(){
			const oldSelect=new MySet(this.value);
			const userSelect=this.userSelect;
			for(let i=0;i<userSelect.length;++i){
				const ele=userSelect[i];
				if(oldSelect.has(ele)){
					oldSelect.delete(ele);
				}else{
					oldSelect.add(ele);
				}
			}
			return oldSelect;
		},
		//显示选择的矩形
		selectRect(){
			if(!this.selecting){
				return null;
			}
			const {
				startY,
				startX,
				endY,
				endX,
			}=this;
			return {
				left:Math.min(endX,startX)+'px',
				top:Math.min(endY,startY)+'px',
				width:Math.abs(endX-startX)+'px',
				height:Math.abs(endY-startY)+'px',
			};
		},
	},
	created(){
		//拉选节流
		this.$_lexmis_mousemove2=throttle(this.$_lexmis_mousemove,16,{trailing:false});
	},
	mounted(){
		this.$nextTick(function(){
			this.scrollTarget=this.$refs.scroll.getVScrollTarget();
		});
	},
	methods:{
		//容器大小变化
		$_lexmis_resize(size){
			this.$emit('resize',size);
		},
		//计算正在选择数据的开始结束索引
		$_lexmis_calcPos(){
			let {startY,endY,lineHeight}=this;
			if(startY>endY){
				//交换值
				startY^=endY;
				endY^=startY;
				startY^=endY;
			}
			this.startPos=Math.max(0,Math.floor(startY/lineHeight));
			this.endPos=Math.ceil(endY/lineHeight);
		},
		//开始选择
		$_lexmis_mousedown({clientX,clientY,ctrlKey,shiftKey}){
			if(this.disable) return;
			this.selecting=true;
			if(!ctrlKey){
				this.$emit('input',new MySet());
			}
			const scroll=this.$refs.scroll;
			const {top,left,height}=scroll.$el.getBoundingClientRect();
			this.maxScroll=this.$refs.container.offsetHeight-height;
			this.endX=this.startX=clientX-left;
			this.endY=this.startY=clientY-top+scroll.getVScrollPosition();
			this.$_lexmis_calcPos();
			if(!shiftKey){
				this.lastSelect=this.userSelect[0];
			}
			window.addEventListener('mousemove',this.$_lexmis_mousemove2,{passive:true});
			window.addEventListener('mouseup',this.$_lexmis_mouseup,{passive:true});
		},
		//选择中
		$_lexmis_mousemove({clientX,clientY}){
			const scroll=this.$refs.scroll;
			const {top,left,height}=scroll.$el.getBoundingClientRect();
			if(clientY<top){
				if(this.autoScrollStatus===0){
					requestAnimationFrame(this.$_lexmis_autoScroll);
				}
				this.autoScrollStatus=1;
			}else if(clientY>top+height){
				if(this.autoScrollStatus===0){
					requestAnimationFrame(this.$_lexmis_autoScroll);
				}
				this.autoScrollStatus=2;
			}else{
				this.autoScrollStatus=0;
				this.endY=clientY-top+scroll.getVScrollPosition();
				this.$_lexmis_calcPos();
			}
			this.endX=clientX-left;
		},
		//选择结束
		$_lexmis_mouseup({shiftKey}){
			const allSelect=this.allSelect;
			if(shiftKey&&this.userSelect.length===1){
				const dataArr=this.dataArr;
				const pos1=dataArr.indexOf(this.lastSelect);
				const pos2=dataArr.indexOf(this.userSelect[0]);
				this.$emit('input',new MySet(dataArr.slice(Math.min(pos1,pos2),Math.max(pos1,pos2)+1)));
			}else{
				this.$emit('input',allSelect);
			}
			this.selecting=false;
			this.autoScrollStatus=0;
			window.removeEventListener('mousemove',this.$_lexmis_mousemove2,{passive:true});
			window.removeEventListener('mouseup',this.$_lexmis_mouseup,{passive:true});
		},
		//每一帧执行的逻辑
		$_lexmis_autoScroll(){
			const autoScrollStatus=this.autoScrollStatus;
			if(autoScrollStatus===0) return;
			const scroll=this.$refs.scroll;
			let scrollPosition=scroll.getVScrollPosition();
			if(autoScrollStatus===1){
				scrollPosition-=14;
				scroll.setVScrollPosition(scrollPosition);
				this.endY=scrollPosition;
			}else{
				scrollPosition=Math.min(scrollPosition+14,this.maxScroll);
				scroll.setVScrollPosition(scrollPosition);
				this.endY=scroll.$el.getBoundingClientRect().height+scrollPosition;
			}
			this.$_lexmis_calcPos();
			requestAnimationFrame(this.$_lexmis_autoScroll);
		},
	}
};
</script>

<style lang="scss">
@import "../../../styles/quasar.variables";
.lexmis-SelectArea{
	user-select:none;
	.lms-ScrollArea_container{
		>.lexmis_rect{
			position:absolute;
			border:1px solid $primary;
			background:rgb(0,126,255,0.3);
		}
	}
}
</style>
